#include "GEnginePCH.h"

#include "Terrain.h"

Terrain::Terrain(void) : TERRAIN_X(128),TERRAIN_Z(128), m_vecScale(10.0f, 3.0f, 10.0f)
{	
	m_dwTerrainVertices = (TERRAIN_X + 1) * (TERRAIN_Z + 1);
	m_dwTerrainPrimitives = TERRAIN_X * TERRAIN_Z * 2;
	//rotateY=0.0f;
}

Terrain::~Terrain(void)
{	
	SAFE_DELETE_ARRAY(aHeightMap);
	SAFE_DELETE_ARRAY(indexBuff);
	invalidateDeviceObjects();
}	

void Terrain::Init( const D3DXMATRIX& matVP )
{

    D3DXCreateTextureFromFile( d3ddev, "../data/terr.bmp", &m_pTexture );
	aHeightMap = new short[m_dwTerrainVertices];
	
	indexBuff = new short [ m_dwTerrainPrimitives * 3 ];

	d3ddev->CreateVertexBuffer(sizeof(VERTEX)*m_dwTerrainVertices,
                                      D3DUSAGE_WRITEONLY,
									  0, 
                                      D3DPOOL_DEFAULT,
                                      &m_pTerrainVB,
                                      NULL);
	

	loadTerrain();
	

	d3ddev->CreateIndexBuffer(sizeof(short)*m_dwTerrainPrimitives*3,
										 D3DUSAGE_WRITEONLY,
										 D3DFMT_INDEX16,
										 D3DPOOL_DEFAULT,
										 &m_pTerrainIB,
										 NULL);

	m_pTerrainIB->Lock(0,0,(void**)&pIndexData,0);

	int k = -1;
	for(DWORD y = 0; y < TERRAIN_Z; ++y)
	{
		for(DWORD x = 0; x < TERRAIN_X; ++x)
		{
			indexBuff[++k] = *pIndexData++ =(short)( x + y * (TERRAIN_X + 1)); //v1
			indexBuff[++k] = *pIndexData++ =(short)( x + (y + 1) * (TERRAIN_X + 1)); //v3
			indexBuff[++k] = *pIndexData++ =(short)( x + 1 + (y + 1) * (TERRAIN_X + 1)); //v4
		
			indexBuff[++k] = *pIndexData++ =(short)( x + y * (TERRAIN_X + 1)); //v1
			indexBuff[++k] = *pIndexData++ =(short)( x + 1 + (y + 1) * (TERRAIN_X + 1)); //v4
			indexBuff[++k] = *pIndexData++ =(short)( x + 1 + y * (TERRAIN_X + 1)); //v2
		}
	}
	m_pTerrainIB->Unlock();
	

	CalcWorld( World, -60.0f, -60.0f, 70.0f, 0.0f, 0.0f , 0.0f, .1f, .1f, .1f);
}

void Terrain::Render( const D3DXMATRIX& matVP )
{	
	//rotateY+=0.004f;
	//d3ddev->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
		//(timeGetTime()%1000)/10000.0f*3.14f

	SetShaderConst(World, matVP);

    d3ddev->SetTexture( 0, m_pTexture );

	d3ddev->SetStreamSource(0,m_pTerrainVB,0,sizeof(VERTEX));
	d3ddev->SetIndices(m_pTerrainIB);
	d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
                                        0,
                                        0,
                                        m_dwTerrainVertices,
                                        0,
	                                    m_dwTerrainPrimitives);	
	
}
void Terrain::loadTerrain(void)
{
	
	std::ifstream heightmap("../data/heightmap.txt");
		//	char ccc[100];
		//sprintf_s(ccc,"%f",pVertexData[x + z * (TERRAIN_X + 1)].y);
		//MessageBox( NULL, ccc, "ERROR", MB_OK | MB_ICONEXCLAMATION );


	m_pTerrainVB->Lock(0,0,(void**)&pVertexData,0);

	
	int kappa=-1;

	for(DWORD z = 0;z < (TERRAIN_Z + 1);++z)
	{
		for(DWORD x = 0;x < (TERRAIN_X + 1);++x)
		{
			float ics,zet;
			pVertexData[x + z * (TERRAIN_X + 1)].x = ics = (float)x*m_vecScale.x;
			pVertexData[x + z * (TERRAIN_X + 1)].z = zet = (float)z*m_vecScale.z;
			heightmap>>aHeightMap[++kappa];

			aHeightMap[kappa] = (short)(aHeightMap[kappa] * m_vecScale.y);

			pVertexData[x + z * (TERRAIN_X + 1)].y = aHeightMap[kappa];

			pVertexData[x + z * (TERRAIN_X + 1)].tu = TERRAIN_Z-(float)z/TERRAIN_Z;
			pVertexData[x + z * (TERRAIN_X + 1)].tv = TERRAIN_X-(float)x/TERRAIN_X;
		}
	}

	m_pTerrainVB->Unlock();
	
	heightmap.close();
}
void Terrain::invalidateDeviceObjects(void)
{
	if( m_pTerrainVB != NULL )
    {
        int nNewRefCount = m_pTerrainVB->Release();

        if( nNewRefCount > 0 )
        {
            static char strError[255];
            sprintf_s( strError, "The Terrain Vertex Buffer object failed to cleanup properly.\n"
                "Release() returned a reference count of %d", nNewRefCount );
            MessageBox( NULL, strError, "ERROR", MB_OK | MB_ICONEXCLAMATION );
        }

		m_pTerrainVB = NULL;
    }

	if( m_pTerrainIB != NULL )
    {
        int nNewRefCount = m_pTerrainIB->Release();

        if( nNewRefCount > 0 )
        {
            static char strError[255];
            sprintf_s( strError, "The Terrain Index Buffer object failed to cleanup properly.\n"
                "Release() returned a reference count of %d", nNewRefCount );
            MessageBox( NULL, strError, "ERROR", MB_OK | MB_ICONEXCLAMATION );
        }

		m_pTerrainIB = NULL;
    }
	
	if( m_pTexture != NULL )
    {
        int nNewRefCount = m_pTexture->Release();

        if( nNewRefCount > 0 )
        {
            static char strError[255];
            sprintf_s( strError, "The Terrain Texture object failed to cleanup properly.\n"
                "Release() returned a reference count of %d", nNewRefCount );
            MessageBox( NULL, strError, "ERROR", MB_OK | MB_ICONEXCLAMATION );
        }

		m_pTexture = NULL;
    }
}
//Watch checkCollision.png
bool Terrain::checkCollision( int mouseX, int mouseY, const D3DXMATRIX &matView, const D3DXMATRIX &matProj, D3DXVECTOR3 &hitPoint)
{ 

	D3DXVECTOR4 vRayOrigin;
	D3DXVECTOR3 vRayDirect;
	D3DXVECTOR3 vRayOrig, vRayDir;
	GetRay( mouseX, mouseY, vRayOrig, vRayDir, matView, matProj);
			
			
	D3DXMATRIX mm;
	D3DXMatrixInverse(&mm ,NULL, &World);
	D3DXVec3Transform(&vRayOrigin, &vRayOrig, &mm);
	D3DXVec3TransformNormal(&vRayDirect, &vRayDir, &mm);
	D3DXVec3Normalize(&vRayDirect, &vRayDirect);

	vRayOrig.x=vRayOrigin.x;
	vRayOrig.y=vRayOrigin.y;		
	vRayOrig.z=vRayOrigin.z;

	vRayDir.x=vRayDirect.x;
	vRayDir.y=vRayDirect.y;
	vRayDir.z=vRayDirect.z;




	
	int nrQuads = m_dwTerrainPrimitives / 2;
	int one, two, three, four;

	float x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4; 
		 
	bool hit;
	for(DWORD z = 0;z < TERRAIN_Z ; ++z)
	{
		for(DWORD x = 0;x < TERRAIN_X ; ++x)
		{
		
			
			one = ( x + z * (TERRAIN_X + 1));
			two = one + 1;
			three = two + TERRAIN_X;
			four = three + 1;
			

			z1 = z * m_vecScale.x;
			x1 = x * m_vecScale.z;
			
			z2 = z1;
			x2 = x1 + m_vecScale.x;

			z3 = z1 + m_vecScale.z;
			x3 = x1;

			z4 = z1 + m_vecScale.z;
			x4 = x2;

			y1 = aHeightMap[one];
			y2 = aHeightMap[two];
			y3 = aHeightMap[three];
			y4 = aHeightMap[four];

			
			
			
			D3DXVECTOR3 a( x1, y1, z1 );
			D3DXVECTOR3 b( x2, y2, z2 );
			D3DXVECTOR3 c( x3, y3, z3 );
			D3DXVECTOR3 d( x4, y4, z4 );
/*
			D3DXVECTOR3 eee( one, two, three );
			printVector(eee);
			D3DXVECTOR3 cee( four, 0.0f , 0.0f );
			printVector(cee);
			printVector(a);
			printVector(b);
			printVector(c);
			printVector(d);*/
			
			//v1 v3 v4
			float fPickT, fPickU, fPickV;
			hit = DoesRayIntersectTriangle( vRayOrig, vRayDir,
							c, d, a,
							fPickT, fPickU, fPickV );	
			//hit = DoesRayIntersectTriangle( D3DXVECTOR3(0,1,0) , D3DXVECTOR3(0,-1,0),
			//	D3DXVECTOR3(-1,0,0) , D3DXVECTOR3(0,0,3) , D3DXVECTOR3(3,0,-10),
			//	fPickT, fPickU, fPickV );	
			if ( hit ) 
			{
				
				hitPoint.x = c.x/m_vecScale.x + World._41 ;
				hitPoint.z = c.z/m_vecScale.z + World._43 ;

				//printVector(hitPoint);
				//printMatrix(out);
				return true;
				//MessageBox( NULL, "Click", "ERROR", MB_OK | MB_ICONEXCLAMATION );
				
				//vezi daca e cel mai apropiat.				
			}

			//v2 v4 v1
			
		
			hit = DoesRayIntersectTriangle( vRayOrig, vRayDir,
					   b, a, d,
						fPickT, fPickU, fPickV );	
			if ( hit ) 
			{			
				
				hitPoint.x = b.x/m_vecScale.x + World._41 ;
				hitPoint.z = b.z/m_vecScale.z + World._43 ;
				//MessageBox( NULL, "Click", "ERROR", MB_OK | MB_ICONEXCLAMATION );
				return true;
				//vezi daca e cel mai apropiat.				
			}	

		}
	}
return false;
}
/*
float Terrain::GetTerrainHeight( float TerX, float TerZ )
{
	D3DXMATRIX mm;
	D3DXMatrixInverse(&mm ,NULL, &World);
	D3DXVECTOR3 vPos(TerX, 0.0f, TerZ);
	D3DXVECTOR4 vPos2;
	D3DXVec3Transform(&vPos2, &vPos, &mm);
	
	//printVector(vPos);
	//printMatrix(mm);
	 //TO DO : sa misc si pe axa Y cu inversul Y-ului terrenului 

	// we first get the height of 4 points of the quad underneath the point
	int x = int(vPos2.x);
	int z = int(vPos2.z);
	
	char w[300];
	sprintf_s(w, "%f %f %f ",vPos2.x, vPos2.y, vPos2.z);
	//MessageBox( NULL, w , "ERROR", MB_OK | MB_ICONEXCLAMATION );

	int one, two, three, four;
	one = ( x + z * (TERRAIN_X + 1));
	two = one + 1;
	three = two + TERRAIN_X;
	four = three + 1;

	float TriY[4];			
	TriY[0] = (aHeightMap[ one ] ) * SCALE_Y ;
	TriY[1] = (aHeightMap[ two ] ) * SCALE_Y ;
	TriY[2] = (aHeightMap[ three ] ) * SCALE_Y;
	TriY[3] = (aHeightMap[ four ] ) * SCALE_Y;

	// find which of the 2 triangles the point is over (depends on how you render)
	// then take the height at a triangle point
	// and adjust by the slope down right-angle edges * distance along that edge.
	float Height;

	float SqX = TerX - x;
	float SqZ = TerZ - z;
	if ( (SqX + SqZ) < 1 )
	{
		Height = TriY[0];
		Height += ( TriY[1] - TriY[0] ) * SqX;
		Height += ( TriY[2] - TriY[0] ) * SqZ;
	} else 
	{
		Height = TriY[3];
		Height += ( TriY[1] - TriY[3] ) * ( 1.0f - SqZ);
		Height += ( TriY[2] - TriY[3] ) * ( 1.0f - SqX);
	}
	return Height;
}*/
float Terrain::GetHeight( float x, float z )
{
    float fTopLeft, fTopRight, fBottomLeft, fBottomRight;
	
	D3DXMATRIX mm;
	D3DXMatrixInverse(&mm ,NULL, &World);
	D3DXVECTOR3 vPos(x, 0.0f, z);
	D3DXVECTOR4 vPos2;
	D3DXVec3Transform(&vPos2, &vPos, &mm);
    
	x = vPos2.x;
	z = vPos2.z;
	
	char w[300];
	sprintf_s(w, "%f %f %f ",vPos2.x, vPos2.y, vPos2.z);
	//MessageBox( NULL, w , "ERROR", MB_OK | MB_ICONEXCLAMATION );
	// Adjust Input Values
    x = x / m_vecScale.x;
	z = z / m_vecScale.z;

    // Make sure we are not OOB
    if ( x < 0.0f || z < 0.0f || x >= (TERRAIN_X + 1) || z >= (TERRAIN_Z + 1) ) return 0.0f;	

    // First retrieve the Heightmap Points
    int ix = (int)x;
    int iz = (int)z;
	
    // Calculate the remainder (percent across quad)
    float fPercentX = x - ((float)ix);
    float fPercentZ = z - ((float)iz);


	// First retrieve the height of each point in the dividing edge
	fTopLeft     = (float)aHeightMap[ix + iz * (TERRAIN_X + 1)] * m_vecScale.y;
	fBottomRight = (float)aHeightMap[(ix + 1) + (iz + 1) * (TERRAIN_X + 1)] * m_vecScale.y;

	// Which triangle of the quad are we in ?
	if ( fPercentX < fPercentZ )
	{
		fBottomLeft = (float)aHeightMap[ix + (iz + 1) * (TERRAIN_X + 1)] * m_vecScale.y;
		fTopRight = fTopLeft + (fBottomRight - fBottomLeft);
    
	} // End if Left Triangle
	else
	{
		fTopRight   = (float)aHeightMap[(ix + 1) + iz * (TERRAIN_X + 1)] * m_vecScale.y;
		fBottomLeft = fTopLeft + (fBottomRight - fTopRight);

	} // End if Right Triangle
    
        
    // Calculate the height interpolated across the top and bottom edges
    float fTopHeight    = fTopLeft    + ((fTopRight - fTopLeft) * fPercentX );
    float fBottomHeight = fBottomLeft + ((fBottomRight - fBottomLeft) * fPercentX );

    // Calculate the resulting height interpolated between the two heights
    return (fTopHeight + ((fBottomHeight - fTopHeight) * fPercentZ ))/27.0f  - 62.3f  ;
}

